from symbide import ModuleManager, PATHS, SHORTCUTS
from symbide.src.widgets import WAIT_ANIMATION

from time import time

from xml.sax.saxutils import escape, unescape
from xml.dom import minidom

import gtk
from image import Image
from latex_widget import LatexWidget
from errorWidget import Error
from widgetOrganizer import WidgetOrganizer
from sympyplot_widget import SympyplotWidget

class Output(WidgetOrganizer, gtk.Frame):
    '''
    Displays the results of the calculations.
    '''
    
    def __init__(self, ancestor, DOM=None):
        gtk.Frame.__init__(self, 'Out')
        WidgetOrganizer.__init__(self, 'Output', ancestor)
        
        self.time = 0
        self.objects = []
        
        #create textview-widget
        self._createTextView(DOM)

    def _createTextView(self, DOM=None):
        self.buffer = gtk.TextBuffer()
        self.view = gtk.TextView(self.buffer)
        self.view.show()
        self.add(self.view)
        
        tag = self.buffer.create_tag('global')
        tag.set_property('size-points',10)
        self.updateTags()
        
        self.buffer.connect('changed', self._event_changed)
        self.view.connect('key_press_event', self._event_keyPress)
        
        self.view.set_editable(False)
        
        self.setdom(DOM)  
    
    def _event_keyPress(self, widget, event):
        '''
        Reacts to different key combinations:
            -if control is pressed it does nothing, because
                it should be possible to copy things.
            -if backspace is pressed twice in a short interval
                the Output is closed
            -if any other 'normal' key is pressed a new cell
                is created and the selected text is copied inside
        '''
        masks = gtk.accelerator_get_default_mod_mask()
        
        events = (('executeWorksheet', 'execute'),
                  ('openWorksheet', 'openWorksheet'),
                  ('saveWorksheet', 'saveWorksheet'),
                  ('saveWorksheetAs', 'saveWorksheetAs'))
        
        for i, j in events:
            keyval, modifier = SHORTCUTS[i]
            if event.keyval==keyval and masks & event.state==modifier:
                getattr(self.getAncestor('Notebook'), j)()
                return True
        
        if event.state & gtk.gdk.CONTROL_MASK:
            return False
        
        #close output if backspace is pressed twice and output is empty
        if event.keyval==ord('\b'):
            t = time()
            dt = t-self.time
            self.time = t
            if 0.1<dt<0.4:
                self._close()
            return True
        
        #add a new cell, if user tries to write in output
        if 33<event.keyval<127 or event.keyval==ord('\n'):
            self._addCell()    
            return True
    
    def _event_changed(self, buffer):
        '''
        Update the styling tags in buffer, and set changed True.
        '''
        
        self.changed = True
        self.updateTags()
    
    def _addCell(self):
        '''
        Adds a new cell under the own cell to the worksheet.
        Any text in the output that is highlighted, is copied into
        the input of the new cell. 
        '''
        
        text = self.getText()
        
        DOM = minidom.Element('cell')
        
        DOM_input = minidom.Element('input')
        DOM_text = minidom.Document().createTextNode(escape(text))
        DOM_input.appendChild(DOM_text)
        
        DOM_output = minidom.Element('output')
        DOM.appendChild(DOM_input)
        DOM.appendChild(DOM_output)
        
        self.getAncestor('Worksheet').addCell(DOM, neighbor=self.ancestor, relPos=1)
        
    def _close(self):
        '''
        Removes this Output.
        '''
        
        self.ancestor.removeChild(self)
        
    def destroy(self):
        '''
        Cleans up everything.
        '''
        
        for i in self.objects:
            i.destroy()
        gtk.Frame.destroy(self)
    
    def clear(self):
        '''
        Destroys all objects stored in self.objects.
        '''
        
        for i in self.objects:
            i.destroy()
        try:
            self.buffer.set_text('')
        except:
            pass
    
    def updateTags(self):
        '''
        Update the styling tags in buffer.
        '''
        
        start, end = self.buffer.get_bounds()
        self.buffer.apply_tag_by_name('global', start, end)
    
    def hasChanged(self):
        '''
        Returns True if itself or any of the objects in self.objects
        have changed.
        '''
        
        return any([i.changed for i in self.objects]+[self.changed])
    
    def setChanged(self, changed):
        '''
        Sets the changed attribut of itself and the objects in
        self.objects
        '''
        
        for i in self.objects+[self]:
            i.changed = changed
    
    def getText(self):
        '''
        Returns the selected text, or if nothing is selected, the
        entire text of the buffer.
        '''
        
        
        self.view.set_editable(True)
        bounds = self.buffer.get_selection_bounds()
        self.view.set_editable(False)
        
        if bounds:
            start, end = bounds
        else:
            start, end = self.buffer.get_bounds()
        text = self.buffer.get_text(start, end)
        return text
    
    def todom(self):
        '''
        Returns a DOM representation of Output.
        '''
        
        #return only a clone, otherwise self.DOM would become
        #unuseable after anyone calls ulink.
        DOM = self.DOM.cloneNode(self.DOM)
        plotNodes = DOM.getElementsByTagName('symbide_plot')
        assert len(plotNodes)==len(self.objects)
        for i in range(len(self.objects)):
            plotNodes[i].parentNode.replaceChild(self.objects[i].todom(),plotNodes[i])
            
        return DOM
    
    def setdom(self, DOM):
        '''
        Replaces the current Output with the Output represented he DOM.
        '''
        
        #if Output empty, turns itself invisible
        self.buffer.set_text('')
        self.objects = []
        
        if not DOM or not DOM.childNodes:
            self.DOM = minidom.Element('output')
            self.DOM.ownerDocument = minidom.Document()
            self.hide()
            return
        
        #take DOM out of the DOM tree
        if DOM.parentNode:
            DOM.parentNode.removeChild(DOM)
            
        #self.DOM = DOM.cloneNode(True)
        self.DOM = DOM
        
        #some shortcuts
        end = self.buffer.get_end_iter
        append = lambda text:self.buffer.insert(end(), text)
        
        self.objects = []
        for node in DOM.childNodes:
            if node.nodeType==node.TEXT_NODE:
                append(unescape(node.nodeValue))
            elif node.nodeType==node.ELEMENT_NODE:
                if node.localName=='symbide_error':
                    error = Error(self.ancestor.getChildren('Input'), node)
                    anchor = self.buffer.create_child_anchor(end())
                    self.view.add_child_at_anchor(error, anchor)
                elif node.localName=='symbide_plot' and ModuleManager.hasModule('sympy'):
                    plotWidget = SympyplotWidget(node)
                    self.objects.append(plotWidget)
                    anchor = self.buffer.create_child_anchor(end())
                    self.view.add_child_at_anchor(plotWidget, anchor)
                elif node.localName=='img':
                    imageWidget = Image(node.getAttribute('src'))
                    anchor = self.buffer.create_child_anchor(end())
                    self.view.add_child_at_anchor(imageWidget, anchor)
                elif node.localName=='latex':
                    latexWidget = LatexWidget(node)
                    anchor = self.buffer.create_child_anchor(end())
                    self.view.add_child_at_anchor(latexWidget, anchor)
                elif node.localName=='math':
                    if ModuleManager.hasModule('Ft'):
                        from Ft.Xml.Xslt import Transform
                        from mathml import MathML
                        pres_mathml = Transform(str(node.toxml()), PATHS['ctop'])
                        DOM = minidom.parseString(pres_mathml).documentElement
                        mathmlWidget = MathML(DOM)
                        anchor = self.buffer.create_child_anchor(end())
                        self.view.add_child_at_anchor(mathmlWidget, anchor)
                    else:
                        append('Ft needed:\n'+node.toxml())
                
        self.ancestor.show_all()
        
    def wait(self):
        image = gtk.Image()
        image.show()
        image.set_from_animation(WAIT_ANIMATION)
        anchor = self.buffer.create_child_anchor(self.buffer.get_end_iter())
        self.view.add_child_at_anchor(image, anchor)
